/*
 *  Filename: i2c.c
 *  Function: i2c program for STM32F103
 *  Created: 2017-06-13
 *  Version: 1.0
 *  Author: Bin
 */

/*-------- Includes --------*/
#include "stm32f10x.h"

#include "i2c.h"
#include "systick.h"

/*-------- Defines --------*/
#define I2C_PORT GPIOA

#define SCL_Pin GPIO_Pin_2
#define SDA_Pin GPIO_Pin_0

#define SDA_Pos 0

// bit write operation
#define SCL_0 I2C_PORT->BRR = SCL_Pin
#define SCL_1 I2C_PORT->BSRR = SCL_Pin
#define SDA_0 I2C_PORT->BRR = SDA_Pin
#define SDA_1 I2C_PORT->BSRR = SDA_Pin
// bit read operation
#define SCL_STATE (I2C_PORT->IDR & SCL_Pin)
#define SDA_STATE (I2C_PORT->IDR & SDA_Pin)

/*-------- Function prototypes --------*/
static void SDA_IN(void);
static void SDA_OUT(void);
static void RCC_Configure(void);
static void GPIO_Configure(void);

/*-------- Private functions --------*/
void SDA_IN(void)
{   // GPIO_Mode_IPU
    uint32_t tmpreg = I2C_PORT->CRL;
    tmpreg &= ~((uint32_t)0x0F << (SDA_Pos * 4));
    tmpreg |= ((uint32_t)0x08 << (SDA_Pos * 4));
    I2C_PORT->CRL = tmpreg;
    SDA_1;
}

void SDA_OUT(void)
{   // GPIO_Mode_Out_PP, 50MHz
    uint32_t tmpreg = I2C_PORT->CRL;
    tmpreg &= ~((uint32_t)0x0F << (SDA_Pos * 4));
    tmpreg |= ((uint32_t)0x03 << (SDA_Pos * 4));
    I2C_PORT->CRL = tmpreg;
}

void RCC_Configure(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
}

void GPIO_Configure(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = SDA_Pin | SCL_Pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(I2C_PORT, &GPIO_InitStructure);
}

/*-------- Exported functions --------*/
void I2C_Init(void)
{
    // Initialize SysTick to enable delay_funtions
    SysTick_Init();

    // Enable GPIO clock and configure GPIO
    RCC_Configure();
    GPIO_Configure();
}

void I2C_Start(void)
{
    SDA_OUT();
    SDA_1;
    SCL_1;
    delay_us(4);
    SDA_0;      // when SCL is high, SDA is negedge
    delay_us(4);
    SCL_0;      // control I2C bus
}

void I2C_Stop(void)
{
    SDA_OUT();
    SCL_0;
    SDA_0;
    delay_us(4);
    SCL_1;
    delay_us(2);// added by myself
    SDA_1;      // when SCL is high, SDA is posedge
    delay_us(2);
}
// if ack then return 0, else return 1
uint8_t I2C_WaitAck(void)
{
    uint8_t wait_time = 0;

    SDA_IN();
    SDA_1; delay_us(1);
    SCL_1; delay_us(1);
    while (SDA_STATE) {
        if (wait_time++ > 250) {
            I2C_Stop();
            return 1;
        }
    }

    SCL_0;
    return 0;
}

void I2C_Ack(void)
{
    SDA_OUT();
    SCL_0;
    SDA_0;
    delay_us(2);
    SCL_1;
    delay_us(2);
    SCL_0;
}

void I2C_NAck(void)
{
    SDA_OUT();
    SCL_0;
    SDA_1;
    delay_us(2);
    SCL_1;
    delay_us(2);
    SCL_0;
}

void I2C_WriteByte(uint8_t data)
{
    uint8_t i;

    SDA_OUT();
    for (i = 0; i < 8; i++) {
        SCL_0;
        if (data & (uint8_t)0x80)
            SDA_1;
        else
            SDA_0;
        data <<= 1;
        delay_us(2);
        SCL_1;
        delay_us(2);
    }
    SCL_0;
    delay_us(2);
}

uint8_t I2C_ReadByte(uint8_t ack)
{
    uint8_t i, data = 0;

    SDA_IN();
    for (i = 0; i < 8; i++) {
        SCL_0;
        delay_us(2);
        SCL_1;
        data <<= 1;
        if (SDA_STATE)
            data++;
        delay_us(2);
    }
    SCL_0;
    delay_us(2);
    if (ack)
        I2C_Ack();
    else
        I2C_NAck();

    return data;
}
